package org.zoomdev.zoom.ioc.impl.factories;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.zoomdev.zoom.common.utils.Classes;
import org.zoomdev.zoom.ioc.IocConstructor;
import org.zoomdev.zoom.ioc.IocException;
import org.zoomdev.zoom.ioc.IocFactory;
import org.zoomdev.zoom.ioc.IocKey;
import org.zoomdev.zoom.ioc.IocTypeKey;
import org.zoomdev.zoom.ioc.IocUtils;
import org.zoomdev.zoom.ioc.modules.CoreIocModule;

import java.lang.reflect.Constructor;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.Collections;

/**
 * 凡是知道具体类是什么，然后用具体类构建的，都属于本分类
 */
public final class ClassIocFactory implements IocFactory, IocFactory.IocKeyContainer {

    private static final Logger log = LoggerFactory.getLogger(CoreIocModule.class);
    static IocConstructor CLASS_CONSTRUCTOR = new IocConstructor() {


        @Override
        public IocKey[] getKeys(IocKey resultKey) {
            return IocKey.NONE_PARAMETER;
        }

        @Override
        public Object newInstance(IocKey resultKey, Object[] args) throws Throwable {
            IocUtils.GenericType gt = (IocUtils.GenericType) resultKey.getType();
            return gt.getActualTypeArguments()[0];
        }
    };

    public static Constructor getConstructor(Type type) {
        Class clazz = Classes.getClass(type);

        Constructor<?>[] constructors = clazz.getConstructors();
        if (constructors.length == 0) {
            return null;
        }
        if (constructors.length == 1) {
            return constructors[0];
        }

        for (Constructor<?> constructor : constructors) {
            if (constructor.getParameterCount() == 0) {
                return constructor;
            }
        }

        return null;
    }

    public static boolean accept(IocKey key) {
        Type type = key.getType();
        return type != null;
    }

    @Override
    public Collection<IocKey> find(Class type) {
        if (type.isInterface()) {
            return null;
        }
        Constructor constructor = getConstructor(type);
        if (constructor == null) {
            return null;
        }
        return Collections.singletonList(new IocTypeKey(type));
    }

    @Override
    public IocConstructor createConstructor(IocKey key) {
        if (key.getType() == null) {
            return null;
        }
        return create0(key);
    }

    IocConstructor create0(IocKey key) {
        Constructor constructor = getConstructor(key.getType());
        if (constructor == null) {
            Type type = key.getType();
            if (type instanceof IocUtils.GenericType) {
                IocUtils.GenericType gt = (IocUtils.GenericType) type;
                if (gt.getRawType() == Class.class) {
                    return CLASS_CONSTRUCTOR;
                }
            }
            return null;
            //throw new IocException("不能对类" + key.getType() + "创建IocConstructor,请提供至少一个可访问的构造函数，否则系统无法判断应该使用哪一个构造函数来创建对象.");
        } else {
            if (!IocKey.checkParameters(constructor.getGenericParameterTypes(), constructor.getParameterAnnotations())) {
                return null;
            }
        }
        try {
            return new ClassIocConstructor(IocKey.fromConstructor(key, constructor), constructor);
        } catch (IocException e) {
            throw new IocException("无法为" + constructor + "创建ioc配置", e);
        }
    }


    public static class ClassIocConstructor implements IocConstructor {

        private final IocKey[] keys;
        private Constructor constructor;

        public ClassIocConstructor(IocKey[] keys, Constructor constructor) {
            this.keys = keys;
            this.constructor = constructor;
            this.constructor.setAccessible(true);
        }


        /**
         * 替换实际类
         *
         * @param type
         */
        public void setType(Class type) {
            Constructor newConstructor = ClassIocFactory.getConstructor(type);
            if (newConstructor == null) {
                throw new IocException("找不到" + type + "的构造函数");
            }

            if (newConstructor.getParameterTypes().length != constructor.getParameterTypes().length) {
                throw new IocException("构造函数的参数不一致");
            }

            this.constructor = newConstructor;
        }

        public IocKey[] getKeys() {
            return keys;
        }

        public Constructor getConstructor() {
            return constructor;
        }

        @Override
        public IocKey[] getKeys(IocKey resultKey) {
            return keys;
        }


        @Override
        public Object newInstance(IocKey resultKey, Object[] args) throws Throwable {
            return constructor.newInstance(args);
        }

        @Override
        public String toString() {
            return "ClassIocConstructor: " + constructor;
        }
    }

}
